"""
Contains our kind of playlist and the controls to navigate in it.

/file pyMusikStack.py
/author Guillaume
/date november 20th, 2012
"""

import unittest
from pickle import Pickler, Unpickler

from pkLibraryElement import pkLibraryElement


class MusikStack():
	""" Kind of playlist of LiraryElement(song, album, artist) """
	
	def __init__(self):
		self._stack = []


	def insert(self, library_element, index = None):
		"""
		Insert library_element at the index position
		/note Behave like append with index as default
		"""
		try:
			self._valid_library_element(library_element)
			if index == None:
				index = len(self._stack)
			else:
				self._valid_index(index)
		except (IndexError, TypeError) as e:
			print("Error:" , e)
		else:
			self._stack.insert(index, library_element)


	def move(self, from_index, to_index):
		""" Move an elements within the musikStack """

		try:
			self._valid_index(from_index)
			self._valid_index(to_index)
		except IndexError as e:
			print("Error:" , e)
		else:
			temp = self._stack[to_index]
			self._stack[to_index] = self._stack[from_index]
			self._stack[from_index] = temp
		

	def remove(self, index):
		""" Remove the element of the musikStack who's at the index position """

		self._valid_index(index)
		del self._stack[index]


	def _valid_index(self, index):

		if index > len(self._stack) or index < 0:
			error_message = " is not between 0 and the musikStack lenght: "
			raise IndexError(str(index) + error_message + str(len(self._stack)))
		

	def _valid_library_element(self, library_element):
		""" verify if library_element given as argument is an instance of pkLibraryElement """
		
		if not isinstance(library_element, pkLibraryElement): 
			raise TypeError(library_element + "is not a pkLibraryElement")


	def clear(self):
		""" Empty the musikStack after saving it, in case of an undo """

		self._save_backup(self._stack)
		self._stack = []


	def go_back_to_backup_stack(self):
		""" Set the current stack to the backup stack """

		temp = self._stack
		self._load_backup()
		self._save_backup(temp)


	def _save_backup(self, stack):
		""" Save the given stack into a backup file """

		with open("../myStacks/.stack_backup", "wb") as backup_file:
			pickler = Pickler(backup_file)
			pickler.dump(stack)


	def _load_backup(self):
		""" Load the stack backup from a backup file"""

		with open("../myStacks/.stack_backup", "rb") as backup_file:
			unpickler = Unpickler(backup_file)
			self._stack = unpickler.load()


	def _get_stack(self):
		cloned_stack = list(self._stack)
		return cloned_stack

	stack = property(_get_stack, None)

class TestMusikStack(unittest.TestCase):

	def setUp(self):
		self.stack = MusikStack()
		self.song0 = pkLibraryElement("song0")
		self.song1 = pkLibraryElement("song1")
		self.song2 = pkLibraryElement("song2")


		self.stack.insert(self.song0)
		self.stack.insert(self.song1)


	def test_insert(self):
		expected_list = [self.song0, self.song1]
		self.assertEqual(self.stack.stack, expected_list)


	def test_insert_at(self):
		self.stack.insert(self.song2, 1)
		expected_list = [self.song0, self.song2, self.song1]
		self.assertEqual(self.stack.stack, expected_list)


	def test_wrong_param_insert(self):
		self.stack.insert("kjghjdrjhgd")
		expected_list = [self.song0, self.song1]
		self.assertEqual(self.stack.stack, expected_list)


	def test_out_of_bounds_insert(self):
		self.stack.insert(self.song2, 3)
		self.stack.insert(self.song2, -1)
		expected_list = [self.song0, self.song1]
		self.assertEqual(self.stack.stack, expected_list)


	def test_move(self):
		self.stack.move(1, 0)
		expected_list = [self.song1, self.song0]
		self.assertEqual(self.stack.stack, expected_list)


	def test_remove(self):
		self.stack.remove(0)
		self.assertEqual(self.stack.stack, [self.song1])


	def test_clear(self):
		self.stack.clear()
		self.assertEqual(self.stack.stack, [])


	def test_backup_clear_go_back_to_backup_stack(self):
		expected_stack = self.stack.stack
		expected_stack = self.get_string_list(expected_stack)

		self.stack.clear()
		self.stack.go_back_to_backup_stack()

		result_stack = self.stack.stack
		result_stack = self.get_string_list(result_stack)

		self.assertEqual(result_stack, expected_stack)


	def test_out_of_bounds_move(self):
		self.stack.move(1, 3)
		self.stack.move(-1, 3)
		expected_list = [self.song0, self.song1]
		self.assertEqual(self.stack.stack, expected_list)


	def get_string_list(self, stack):
		str_stack = []
		for song in stack:
			str_stack.append(song.get_title())
		return str_stack



if __name__ == "__main__":
	unittest.main()
